//    Copyright (c) 2009 Elwyn Malethan
//
//    This file is part of SeemoreJ.
//
//    SeemoreJ is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    SeemoreJ is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with SeemoreJ.  If not, see <http://www.gnu.org/licenses/>.

package com.malethan.seemorej.hibernate;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * <p><strong>Based on a class from the Spring Framework</strong></p>
 * <p/>
 * <p>Filter base class that guarantees to be just executed once per request,
 * on any servlet container. It provides a <code>doFilterInternal</code>
 * method with HttpServletRequest and HttpServletResponse arguments.</p>
 * <p/>
 * <p>The <code>getAlreadyFilteredAttributeName</code> method determines how
 * to identify that a request is already filtered. The default implementation
 * is based on the configured name of the concrete filter instance.</p>
 */
public abstract class OncePerRequestFilter implements Filter
{
    //----------------------------------------------------------------------- Static Properties and Constants

    /**
     * Suffix that gets appended to the filter name for the
     * "already filtered" request attribute.
     *
     * @see #getAlreadyFilteredAttributeName
     */
    public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";

    //----------------------------------------------------------------------- Static Methods
    //----------------------------------------------------------------------- Instance Properties

    FilterConfig filterConfig;

    //----------------------------------------------------------------------- Constructors
    //----------------------------------------------------------------------- Getters and Setters

    public final FilterConfig getFilterConfig()
    {
        return filterConfig;
    }

    //----------------------------------------------------------------------- Instance Methods

    public final void init(FilterConfig filterConfig) throws ServletException
    {
        this.filterConfig = filterConfig;
    }

    /**
     * This doFilter implementation stores a request attribute for
     * "already filtered", proceeding without filtering again if the
     * attribute is already there.
     *
     * @see #getAlreadyFilteredAttributeName
     * @see #shouldNotFilter
     * @see #doFilterInternal
     */
    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException
    {

        if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse))
        {
            throw new ServletException("OncePerRequestFilter just supports HTTP requests");
        }
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
        if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest))
        {
            // proceed without invoking this filter
            filterChain.doFilter(request, response);
        }
        else
        {
            // invoke this filter
            request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
            doFilterInternal(httpRequest, httpResponse, filterChain);
        }
    }

    public final void destroy()
    {
        this.filterConfig = null;
    }

    /**
     * Return the name of the request attribute that identifies that a request
     * is already filtered. Default implementation takes the configured name
     * of the concrete filter instance and appends ".FILTERED".
     *
     * @see #ALREADY_FILTERED_SUFFIX
     */
    protected String getAlreadyFilteredAttributeName()
    {
        return this.filterConfig.getFilterName() + ALREADY_FILTERED_SUFFIX;
    }

    /**
     * Can return true to avoid filtering of the given request.
     * The default implementation always returns false.
     * Can be overridden in subclasses for custom filtering control.
     *
     * @param request current HTTP request
     * @return whether the given request should <i>not</i> be filtered
     * @throws ServletException in case of errors
     */
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException
    {
        return false;
    }

    /**
     * Same contract as for doFilter, but guaranteed to be just invoked once per
     * request. Provides HttpServletRequest and HttpServletResponse arguments
     * instead of the default ServletRequest and ServletResponse ones.
     */
    protected abstract void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException;

}
